﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Xceed.Words.NET;

namespace EPlusReport
{
    public class DocxReport : IDisposable
    {

        //private const string DocumentSampleResourcesDirectory = Program.SampleDirectory + @"Document\Resources\";
        //private const string DocumentSampleOutputDirectory = Program.SampleDirectory + @"Document\Output\";
        public System.Data.DataTable dtData = null;
        public bool isprotect = false;
        public string password = "";
        DocX document = null;

        bool isNewFile = false;

        public System.Collections.Hashtable htVar = new System.Collections.Hashtable();

        #region Public Methods

        public void Open(string outfile, string template)
        {
            if (!string.IsNullOrEmpty(template))
            {
                if (!System.IO.File.Exists(template))
                {
                    document = DocX.Create(template, DocumentTypes.Document);
                    var p = document.InsertParagraph("Report Var");
                    p.FontSize(15d);
                    p.SpacingAfter(50d);
                    foreach (System.Data.DataColumn dc in dtData.Columns)
                    {
                        p.AppendLine("@0 = {@0}".Replace("@0", dc.ColumnName));
                    }
                    document.Save();
                }
            }

            document = DocX.Create(outfile, DocumentTypes.Document);
            if (!string.IsNullOrEmpty(template))
            {
                if (System.IO.File.Exists(template))
                {
                    document.ApplyTemplate(template);
                }
            }
            document.AddCustomProperty(new CustomProperty("CompanyName", "ocbcwh"));
            document.AddCustomProperty(new CustomProperty("Product", "docx"));
            document.AddCustomProperty(new CustomProperty("Date", DateTime.Now));
            //document.AddCustomProperty(new CustomProperty("Address", ""));
        }

        public void save()
        {
            if (isprotect)
            {
                document.AddProtection(EditRestrictions.readOnly);
            }
            if (!string.IsNullOrEmpty(password))
            {
                document.AddPasswordProtection(EditRestrictions.readOnly, password);
            }
            document.Save();
        }

        public void setData(System.Data.DataTable dt)
        {
            dtData = dt;
        }

        /// <summary>
        /// Load a document and replace texts following a replace pattern.
        /// </summary>
        public void ReplaceText(string pattern)
        {
            // Check if all the replace patterns are used in the loaded document.
            foreach (string key in document.FindUniqueByPattern(pattern.Replace("*", "\\S+?"), RegexOptions.IgnoreCase).ToArray())
            {
                document.ReplaceText(key, ReplaceFunc, false, RegexOptions.IgnoreCase, null, new Formatting(), MatchFormattingOptions.SubsetMatch);
            }
        }

        /// <summary>
        /// Load a document and replace texts following a replace pattern.
        /// </summary>
        public void ReplaceHeader(Header head, string pattern)
        {
            // Check if all the replace patterns are used in the loaded document.
            if (head != null)
                foreach (string key in head.FindUniqueByPattern(pattern.Replace("*", "\\S+?"), RegexOptions.IgnoreCase).ToArray())
                {
                    head.ReplaceText(key, ReplaceFunc, false, RegexOptions.IgnoreCase, null, new Formatting(), MatchFormattingOptions.SubsetMatch);
                }
        }

        public void ReplaceFoot(Footer foot, string pattern)
        {
            // Check if all the replace patterns are used in the loaded document.
            if (foot != null)
                foreach (string key in foot.FindUniqueByPattern(pattern.Replace("*", "\\S+?"), RegexOptions.IgnoreCase).ToArray())
                {
                    foot.ReplaceText(key, ReplaceFunc, false, RegexOptions.IgnoreCase, null, new Formatting(), MatchFormattingOptions.SubsetMatch);
                }
        }


        int icurrentrow = 0;

        ///// <summary>
        ///// Add a template to a document.
        ///// </summary>
        //public static void ApplyTemplate()
        //{
        //    Console.WriteLine("\tApplyTemplate()");

        //    // Create a new document.
        //    using (DocX document = DocX.Create(DocumentSample.DocumentSampleOutputDirectory + @"ApplyTemplate.docx"))
        //    {
        //        // The path to a template document,
        //        var templatePath = DocumentSample.DocumentSampleResourcesDirectory + @"Template.docx";

        //        // Apply a template to the document based on a path.
        //        document.ApplyTemplate(templatePath);

        //        // Add a paragraph at the end of the template.
        //        document.InsertParagraph("This paragraph is not part of the template.").FontSize(15d).UnderlineStyle(UnderlineStyle.singleLine).SpacingBefore(50d);

        //        // Save this document to disk.
        //        document.Save();
        //        Console.WriteLine("\tCreated: ApplyTemplate.docx\n");
        //    }
        //}

        ///// <summary>
        ///// Insert a document at the end of another document.
        ///// </summary>
        //public static void AppendDocument()
        //{
        //    Console.WriteLine("\tAppendDocument()");

        //    // Load the first document.
        //    using (DocX document1 = DocX.Load(DocumentSample.DocumentSampleResourcesDirectory + @"First.docx"))
        //    {
        //        // Load the second document.
        //        using (DocX document2 = DocX.Load(DocumentSample.DocumentSampleResourcesDirectory + @"Second.docx"))
        //        {
        //            // Insert a document at the end of another document.
        //            // When true, document is added at the end. When false, document is added at beginning.
        //            document1.InsertDocument(document2, true);

        //            // Save this document to disk.
        //            document1.SaveAs(DocumentSample.DocumentSampleOutputDirectory + @"AppendDocument.docx");
        //            Console.WriteLine("\tCreated: AppendDocument.docx\n");
        //        }
        //    }
        //}

        #endregion

        #region Private Methods

        private string ReplaceRow(string findStr)
        {
            System.Data.DataRow dr = dtData.Rows[icurrentrow];
            string c = findStr.Substring(1, findStr.Length - 2);
            if (dtData.Columns.Contains(c))
            {
                findStr = string.Concat(dr[c]);
            }
            return findStr;
        }

        private string ReplaceFunc(string findStr)
        {
            string c = findStr.Substring(1, findStr.Length - 2);
            var ff = c.Split(':');

            if (htVar.ContainsKey(ff[0]))
            {
                return htVar[ff[0]].ToString();
            }

            int irow = 0;
            if (ff.Length > 1) irow = int.Parse(ff[1]);
            System.Data.DataRow dr = dtData.Rows.Count > irow ? dtData.Rows[irow] : dtData.NewRow();
            if (dtData.Columns.Contains(ff[0]))
            {
                findStr = string.Concat(dr[ff[0]]);
            }
            return findStr;
        }

        private string updatePic(string findStr)
        {
            string pictype = "";
            string c = findStr.Substring(1, findStr.Length - 2);
            string[] cc = c.Split('@');
            var ff = cc[0].Split(':');

            if (cc.Length > 1)
                pictype = cc[1];

            int iheight = 50;
            if (cc.Length > 2)
            {
                if (!string.IsNullOrEmpty(cc[2]))
                {
                    int.TryParse(cc[2], out iheight);
                }
            }
            bool drawText = true;
            if (cc.Length > 3)
            {
                drawText = (cc[3].ToLower() == "false" || string.IsNullOrEmpty(cc[3]));
            }

            if (htVar.ContainsKey(ff[0]))
            {
                return htVar[ff[0]].ToString();
            }

            int irow = 0;
            if (ff.Length > 1) irow = int.Parse(ff[1]);
            System.Data.DataRow dr = dtData.Rows.Count > irow ? dtData.Rows[irow] : dtData.NewRow();
            if (dtData.Columns.Contains(ff[0]))
            {
                if (pictype.ToLower() == "code39" || pictype.ToLower() == "br")
                {
                    if (curCell != null)
                    {
                        curCell.ReplaceText(findStr, "", false, RegexOptions.IgnoreCase, null, new Formatting(), MatchFormattingOptions.SubsetMatch, false, false);
                        System.IO.MemoryStream ms = new System.IO.MemoryStream(barcode.Code39.GetCode(string.Concat(dr[ff[0]]), drawText, iheight));
                        AddPicture(curCell.Paragraphs[0], ms);
                        ms.Close();
                    }
                    findStr = "";
                }
                else
                    if (pictype.ToLower() == "code128")
                    {
                        if (curCell != null)
                        {
                            curCell.ReplaceText(findStr, "", false, RegexOptions.IgnoreCase, null, new Formatting(), MatchFormattingOptions.SubsetMatch, false, false);
                            System.IO.MemoryStream ms = new System.IO.MemoryStream(barcode.Code128.GetCode(string.Concat(dr[ff[0]]), drawText, iheight));
                            AddPicture(curCell.Paragraphs[0], ms);
                            ms.Close();
                        }
                        findStr = "";
                    }
                    else
                        if (pictype.ToLower() == "qr")
                        {
                            if (curCell != null)
                            {
                                curCell.ReplaceText(findStr, "", false, RegexOptions.IgnoreCase, null, new Formatting(), MatchFormattingOptions.SubsetMatch, false, false);
                                System.IO.MemoryStream ms = new System.IO.MemoryStream(QRCODE.GetCode(string.Concat(dr[ff[0]]), "", iheight));
                                AddPicture(curCell.Paragraphs[0], ms);
                                ms.Close();
                            }
                            findStr = "";
                        }
                        else
                            if (pictype.ToLower() == "file")
                            {
                                if (curCell != null)
                                {
                                    string imageFile = string.Concat(dr[ff[0]]);
                                    if (System.IO.File.Exists(imageFile))
                                    {
                                        curCell.ReplaceText(findStr, "", false, RegexOptions.IgnoreCase, null, new Formatting(), MatchFormattingOptions.SubsetMatch, false, false);
                                        System.IO.MemoryStream ms = new System.IO.MemoryStream(System.IO.File.ReadAllBytes(imageFile));
                                        AddPicture(curCell.Paragraphs[0], ms);
                                        ms.Close();
                                    }
                                }
                                findStr = "";
                            }
                            else
                                if (pictype.ToLower() == "image")
                                {
                                    if (curCell != null)
                                    {
                                        curCell.ReplaceText(findStr, "", false, RegexOptions.IgnoreCase, null, new Formatting(), MatchFormattingOptions.SubsetMatch, false, false);
                                        System.IO.MemoryStream ms = new System.IO.MemoryStream((byte[])dr[ff[0]]);
                                        AddPicture(curCell.Paragraphs[0], ms);
                                        ms.Close();
                                    }
                                    findStr = "";
                                }
                                else
                                {
                                    curCell.ReplaceText(findStr, string.Concat(dr[ff[0]]), false, RegexOptions.IgnoreCase, null, new Formatting(), MatchFormattingOptions.SubsetMatch, false, false);
                                }
                //findStr = string.Concat(dr[ff[0]]);
            }
            return findStr;
        }

        #endregion

        #region Public Methods

        Cell curCell = null;

        /// <summary>
        /// Update Table in Head,Foot,Body
        /// </summary>
        public void update_image()
        {
            update_headtable(document.Headers.First);
            update_headtable(document.Headers.Odd);
            update_headtable(document.Headers.Even);
            update_foottable(document.Footers.First);
            update_foottable(document.Footers.Odd);
            update_foottable(document.Footers.Even);
            update_tables(document.Tables.ToArray());
        }

        /// <summary>
        /// Update Head Tables
        /// </summary>
        /// <param name="head"></param>
        public void update_headtable(Header head)
        {
            if (head == null) return;
            update_tables(head.Tables.ToArray());
            foreach (string k in head.FindUniqueByPattern("{(\\S+)}", RegexOptions.IgnoreCase))
            {
                head.ReplaceText(k, ReplaceFunc, false, RegexOptions.IgnoreCase, null, null, MatchFormattingOptions.SubsetMatch);
            }

        }

        /// <summary>
        /// Update Tables
        /// </summary>
        /// <param name="tbs"></param>
        public void update_tables(Table[] tbs)
        {
            for (int i = 0; i < tbs.Length; i++)
            {
                var table = tbs[i];
                foreach (var r in table.Rows)
                {
                    foreach (var c in r.Cells)
                    {
                        curCell = c;
                        foreach (string k in c.FindUniqueByPattern("{(\\S+)@(\\S+)}", RegexOptions.IgnoreCase))
                        {
                            updatePic(k);
                            break;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Update Foot Tables
        /// </summary>
        /// <param name="foot"></param>
        public void update_foottable(Footer foot)
        {
            if (foot == null) return;
            update_tables(foot.Tables.ToArray());

            foreach (string k in foot.FindUniqueByPattern("{(\\S+)}", RegexOptions.IgnoreCase))
            {
                foot.ReplaceText(k, ReplaceFunc, false, RegexOptions.IgnoreCase, null, null, MatchFormattingOptions.SubsetMatch);
            }
        }



        /// <summary>
        /// Add a picture loaded from disk or stream to a document.
        /// </summary>
        public void AddPicture(Paragraph p, System.IO.MemoryStream ms)
        {
            var image = document.AddImage(ms);
            Picture picture = image.CreatePicture();
            p.AppendPicture(picture);
        }

        #endregion


        public void Dispose()
        {
            if (document != null) document.Dispose();
        }
    }
}
